#include <mpi.h>

#include "GRASP.h"
#include "Kruskal.h"
#include "FileSTS.h"



double startwtime = 0.0, endwtime;
int melhorIter = 0;


int main(int argc, char** argv){
	if(argc == 4){
		int size, rank;
		MPI_Status status;
	
		MPI_Init(&argc, &argv);
		MPI_Comm_size(MPI_COMM_WORLD, &size);
		MPI_Comm_rank(MPI_COMM_WORLD, &rank);
	
                
                int MAX_ITER = 120;
                int CALCULO_ITER = MAX_ITER/size;
                int RESTO = MAX_ITER%size;
                
                if(rank < RESTO){
                    CALCULO_ITER++;
                }
                
		srand(time(NULL));
		
		//Inicializando os dados de arquivo
		Solucao::loadEntrada(argv[1]);
		double alpha = atof(argv[2]);
		GRASP g(Solucao::tamanho, alpha);
		
		//Pegando o tempo inicial
		if(rank == 0)
			startwtime = MPI_Wtime();

		SolucaoGRASPSTS *sbest = NULL;
                
                printf("Processo %d fazendo %d iteracoes\n",rank, CALCULO_ITER);
                
		for(int i=0; i< CALCULO_ITER; i++){
			//Fase de Construcao e Busca Local.
			SolucaoGRASPSTS *sol = g.construcao();
			SolucaoGRASPSTS *sbl = g.descida(sol);
			delete sol;
                        
                        int nProcs = size;
                        
                        if(i == CALCULO_ITER - 1){
                            if(RESTO != 0){
                                nProcs = RESTO;
                            }
                        }
                        
			char *data = NULL;
			double aval;
			//Sincroniza��o dos resultados.
			if(rank == 0){
				data = new char[Solucao::tamanho + 1];
				//Recebendo dos clientes.
				for(int j=1; j<nProcs; j++){
					MPI_Recv(data, (Solucao::tamanho + 1), MPI_CHAR, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, &status);
					MPI_Recv(&aval, 1, MPI_DOUBLE, MPI_ANY_SOURCE, 1, MPI_COMM_WORLD, &status);
					if(aval < sbl->avaliacao){
						delete sbl;
						sbl = new SolucaoGRASPSTS(data);
						sbl->avaliacao = aval; 
					}
				}
				delete data;
				//Enviando o melhor para os clientes.
				data = sbl->print();
				aval = sbl->avaliacao;
				for(int j=1; j<nProcs; j++){
					MPI_Send(data, (Solucao::tamanho + 1), MPI_CHAR, j, 2, MPI_COMM_WORLD);
					MPI_Send(&aval, 1, MPI_DOUBLE, j, 3, MPI_COMM_WORLD);
				}
			}else{
				data = sbl->print();
				//Enviando a sua melhor solucao para o servidor.
				MPI_Send(data, (Solucao::tamanho + 1), MPI_CHAR, 0, 0, MPI_COMM_WORLD);
				MPI_Send(&sbl->avaliacao, 1, MPI_DOUBLE, 0, 1, MPI_COMM_WORLD);
				
				data = new char[Solucao::tamanho + 1];
				//Recebendo a melhor solucao do servidor. 
				MPI_Recv(data, (Solucao::tamanho+1), MPI_CHAR, 0, 2, MPI_COMM_WORLD, &status);
				MPI_Recv(&aval, 1, MPI_DOUBLE, 0, 3, MPI_COMM_WORLD, &status);
				
				delete sbl;
				sbl = new SolucaoGRASPSTS(data);
				sbl->avaliacao = aval;
				delete data;
			}

			//Executando o VNS.
			SolucaoGRASPSTS *svns = g.ILS(sbl);
			//SolucaoGRASPSTS *svns = sbl;

			//Sincroniza��o dos Resultados
			if(rank == 0){
				data = new char[Solucao::tamanho + 1];
				//Recebendo dos clientes.
				for(int j=1; j<nProcs; j++){
					MPI_Recv(data, (Solucao::tamanho + 1), MPI_CHAR, MPI_ANY_SOURCE, 4, MPI_COMM_WORLD, &status);
					MPI_Recv(&aval, 1, MPI_DOUBLE, MPI_ANY_SOURCE, 5, MPI_COMM_WORLD, &status);
					if(aval < svns->avaliacao){
						delete svns;
						svns = new SolucaoGRASPSTS(data);
						svns->avaliacao = aval;
					}
				}
				if(sbest == NULL){
					sbest = svns;
					melhorIter = i;
				}else{
					if(svns->avaliacao < sbest->avaliacao){
						delete sbest;
						sbest = svns;
						melhorIter = i;
					}else
						delete svns;
				}
				delete data;
			}else{
				data = svns->print();
				//Enviando a sua melhor solucao para o servidor.
				MPI_Send(data, (Solucao::tamanho + 1), MPI_CHAR, 0, 4, MPI_COMM_WORLD);
				MPI_Send(&svns->avaliacao, 1, MPI_DOUBLE, 0, 5, MPI_COMM_WORLD);
				delete svns;
			}
		}
		if(rank == 0){
			endwtime = MPI_Wtime();
			FileSTS::printInstancia(argv[3], size, alpha, sbest->tamanho, sbest->numClient, melhorIter, sbest->avaliacao, (int)((endwtime - startwtime) * 1000));
			printf("Final: %s F: %f Tempo: %f\n", sbest->print(), sbest->avaliacao, endwtime - startwtime);
		}
		
	}else{
		puts("<usage: grasp nameFile alpha estrategia>");
	}
	MPI_Finalize();
	return 0;
}

